#include "nr_io_mongodb.h"

QString string_to_QString(const string &s)
{
    return QString(QString::fromLocal8Bit(s.c_str()));
}
string QString_to_string(const QString &s)
{
    //QTextCodec *code = QTextCodec::codecForName("gb18030");
    //if (code)    
    //    string strStd= code->fromUnicode(qstr).data();
    return string((const char *)s.toLocal8Bit());
}

nr_io_mongodb::nr_io_mongodb(void)
    : m_is_connected(false)
{
}

nr_io_mongodb::~nr_io_mongodb(void)
{
}

nr_error_t nr_io_mongodb::connect()
{
    // Try to connect to the database
    nr_error_t status;
    string errmsg;
    if (m_db_client.connect(NR_STRING_Q2M(m_host), errmsg))
    {
        m_is_connected = true;
        status = NR_ERROR_NONE;
    }
    else
    {
        status = NR_ERROR_DATABASE_CONNECT_FAILED;
    }

    return status;
}

nr_error_t nr_io_mongodb::write_data(nr_data* data)
{
    if (!m_is_connected)
        return NR_ERROR_DATABASE_NOT_CONNECT;

    if (data == NULL)
        return NR_ERROR_NOT_VALID_PARAMETER;

    BSONObjBuilder obj_builder;

    obj_builder.append("date_time", Date_t((unsigned long long)data->time().toMSecsSinceEpoch()));

    // Append each element
    for(QVariantHash::iterator it = data->args().begin();
        it != data->args().end(); ++it)
    {
        // Should know the accurate type of the QVariant
        append_variant(obj_builder, it.key(), it.value());
    }

    // Get the object
    BSONObj obj = obj_builder.obj();

    // Use the use id as the collection name
    QString db_ns = QString("parameters.") + data->device_id();
    m_db_client.insert(NR_STRING_Q2M(db_ns), obj);

    string error = m_db_client.getLastError();
    if (!error.empty())
    {
        qDebug() << "MongoDB insert error : " << error.c_str();
    }

    return NR_ERROR_NONE;
}

nr_error_t nr_io_mongodb::write_event(nr_event* evt)
{
    //events.APMD7XXX.....
    return NR_ERROR_NOT_IMPLEMENTED;
}

nr_error_t nr_io_mongodb::query_data(const nr_device_id_t& device_id, QDateTime &begin_time, 
    QDateTime &end_time, QList<nr_data*>& data_list)
{
    if (!m_is_connected)
        return NR_ERROR_DATABASE_NOT_CONNECT;

    QString db_ns = QString("parameters.") + device_id;

    Date_t mongo_start_time(begin_time.toMSecsSinceEpoch());
    Date_t mongo_end_time(end_time.toMSecsSinceEpoch());

    // Query the data between the start time and end time
    auto_ptr<DBClientCursor> cursor =
        m_db_client.query(NR_STRING_Q2M(db_ns), QUERY("date_time" << GTE << mongo_start_time << LTE << mongo_end_time));

    // Read each bson object from the mongo cursor
    while (cursor->more()) 
    {
        BSONObj obj = cursor->next();

        // Create the data
        nr_data* data = nr_data::create();
        data->device_id() = device_id;

        // Find each element
        BSONObjIterator it(obj);
        while(it.more())
        {
            BSONElement elem = it.next();

            // Convert the bson object to nr_data
            element_to_data(elem, *data);
        }

        // Add to the data list
        data_list.push_back(data);
    }

    return NR_ERROR_NONE;
}

nr_error_t nr_io_mongodb::query_event(const nr_device_id_t& device_id, QDateTime &begin_time, 
    QDateTime &end_time, QList<nr_event*>& data_list)
{
    return NR_ERROR_NOT_IMPLEMENTED;
}


nr_error_t nr_io_mongodb::read_device_types( nr_device_types& device_types )
{
    if (!m_is_connected)
        return NR_ERROR_DATABASE_NOT_CONNECT;

    string db_ns = "DeviceSchema.device_types";

    // Query the data
    auto_ptr<DBClientCursor> cursor = m_db_client.query(db_ns);

    // Read each bson object from the mongo cursor
    while (cursor->more())
    {
        BSONObj obj = cursor->next();
        nr_device_type *devicetype = new nr_device_type;
        bool ret = read_document_DeviceType( obj , *devicetype );
        Q_ASSERT(ret);
        device_types.insert(devicetype->id(),devicetype);
    }

    //store the device_types , it is useful in read_device_items.
    m_device_types = &device_types;

    return NR_ERROR_NONE;
}

nr_error_t nr_io_mongodb::write_device_types( nr_device_types& device_types )
{
    if (!m_is_connected)
        return NR_ERROR_DATABASE_NOT_CONNECT;

    //remove data in mongodb first
    string db_ns = "DeviceSchema.device_types";
    m_db_client.remove( db_ns , Query() );

    foreach(nr_device_type* devicetype, device_types)
    {
        BSONObjBuilder &objbuilder = write_document_DeviceType(devicetype);

        m_db_client.insert(db_ns, objbuilder.obj());
    }

    string error = m_db_client.getLastError();
    if (!error.empty())
    {
        qDebug() << "MongoDB insert error : " << error.c_str();
    }

    return NR_ERROR_NONE;
}


nr_error_t nr_io_mongodb::read_device_items( nr_device_item_server& item_server )
{
    if (!m_is_connected)
        return NR_ERROR_DATABASE_NOT_CONNECT;

    string db_ns = "DeviceItems.device_item_server";

    // Query the data
    auto_ptr<DBClientCursor> cursor = m_db_client.query(db_ns);

    // Read each bson object from the mongo cursor
    while (cursor->more()) 
    {
        BSONObj obj = cursor->next();
        bool ret = read_document_DeviceItemServer( obj , item_server );
        Q_ASSERT(ret);
    }

    return NR_ERROR_NONE;
}





nr_error_t nr_io_mongodb::write_device_items( nr_device_item_server& item_server )
{
    if (!m_is_connected)
        return NR_ERROR_DATABASE_NOT_CONNECT;

    //remove data in mongodb first
    string db_ns = "DeviceItems.device_item_server";
    m_db_client.remove( db_ns , Query() );

    BSONObjBuilder &objbuilder = write_document_DeviceItemServer(&item_server);
    m_db_client.insert(db_ns, objbuilder.obj());

    string error = m_db_client.getLastError();
    if (!error.empty())
    {
        qDebug() << "MongoDB insert error : " << error.c_str();
    }

    return NR_ERROR_NONE;
}

nr_device_type* nr_io_mongodb::get_device_type( const nr_id_t &type_id )
{
    nr_device_types::iterator it = m_device_types->find(type_id);
    if (it != m_device_types->end())
    {
        return it.value();
    }
    else
    {
        Q_ASSERT(false);
        return NULL;
    }
}

bool nr_io_mongodb::parse_elementdata( BSONElement& elem, QVariant& variant )
{
    switch(elem.type())
    {
    case mongo::NumberDouble:
        variant = QVariant(elem.Double());
        break;
    case mongo::String:
        variant = QVariant(string_to_QString(elem.String()));
        break;
    case mongo::Bool:
        variant = QVariant(elem.Bool());
        break;
    case mongo::NumberInt:
        variant = QVariant(elem.Int());
        break;
    case mongo::NumberLong:
        variant = QVariant(elem.Long());
        break;
    case mongo::Date:
        {
            QDateTime date_time = QDateTime::fromMSecsSinceEpoch(elem.Date().millis);
            variant = QVariant(date_time);
        }
        break;
    default: // Ignore the type which we not used 
        return false;
    }

    return true;
}

bool nr_io_mongodb::append_BSONObj(BSONObjBuilder& obj_builder, const QString& key, BSONObj& obj)
{
    obj_builder.append(key.toStdString(), obj);
    return true;
}

bool nr_io_mongodb::append_variant(BSONObjBuilder& obj_builder, const QString& key, const QVariant& val)
{
    switch(val.type())
    {
    case QVariant::Bool:
        obj_builder.append(NR_STRING_Q2M(key), val.value<bool>()); 
        break;
    case QVariant::Int: 
        obj_builder.append(NR_STRING_Q2M(key), val.value<int>()); 
        break;
    case QVariant::UInt:
        obj_builder.append(NR_STRING_Q2M(key), val.value<unsigned int>()); 
        break;
    case QVariant::LongLong:
        obj_builder.append(NR_STRING_Q2M(key), val.value<long long>()); 
        break;
    case QVariant::ULongLong:
        obj_builder.append(NR_STRING_Q2M(key), val.value<long long>()); 
        break;
    case QVariant::Double:
        obj_builder.append(NR_STRING_Q2M(key), val.value<double>()); 
        break;
    case QVariant::Char:
        obj_builder.append(NR_STRING_Q2M(key), val.value<char>()); 
        break;
    case QVariant::String:
        {
            string str1 = NR_STRING_Q2M(key);
            string str2 = QString_to_string(val.value<QString>());
            obj_builder.append(NR_STRING_Q2M(key), str2/*NR_STRING_Q2M(val.value<QString>())*/); 
            break;
        }
    default:
        // Not support yet!!!
        Q_ASSERT(false);
    }

    return true;
}

bool nr_io_mongodb::element_to_data(BSONElement& elem, nr_data& data)
{
    switch(elem.type())
    {
    case mongo::NumberDouble:
        data.args().insert(NR_STRING_M2Q(elem.fieldName()), QVariant(elem.Double()));
        break;
    case mongo::String:
        data.args().insert(NR_STRING_M2Q(elem.fieldName()), QVariant(QString::fromStdString(elem.String())));
        break;
    case mongo::Bool:
        data.args().insert(NR_STRING_M2Q(elem.fieldName()), QVariant(elem.Bool()));
        break;
    case mongo::NumberInt:
        data.args().insert(NR_STRING_M2Q(elem.fieldName()), QVariant(elem.Int()));
        break;
    case mongo::NumberLong:
        data.args().insert(NR_STRING_M2Q(elem.fieldName()), QVariant(elem.Long()));
        break;
    case mongo::Date:
        {
            // Special case for time field
            QString field_name = NR_STRING_M2Q(elem.fieldName());
            QDateTime date_time = QDateTime::fromMSecsSinceEpoch(elem.Date().millis);
            if (field_name == "date_time")
            {
                data.time() = date_time;
            }
            else
            {
                data.args().insert(field_name, QVariant(date_time));
            }
        }
        break;
    default: // Ignore the type which we not used 
        return false;
    }

    return true;
}

bool nr_io_mongodb::read_document_Item( BSONElement& element, nr_device_type_property& devicetypeproperty )
{
    BSONObj obj_item = element.Obj();
    BSONObjIterator it_item(obj_item);
    while (it_item.more())
    {
        BSONElement elem_item = it_item.next();

        if (QString(elem_item.fieldName())=="id")
        {
            devicetypeproperty.id() = elem_item.numberInt();
        }
        else if (QString(elem_item.fieldName())=="name")
        {
            //QString::fromStdString(elem_item.String());
            devicetypeproperty.name() = string_to_QString(elem_item.String());
        }
        else if (QString(elem_item.fieldName())=="text")
        {
            devicetypeproperty.text() = string_to_QString(elem_item.String());
        }
        else if (QString(elem_item.fieldName())=="category")
        {
            devicetypeproperty.category() = string_to_QString(elem_item.String());
        }
        else if (QString(elem_item.fieldName())=="description")
        {
            devicetypeproperty.description() = string_to_QString(elem_item.String());
        }
        else if (QString(elem_item.fieldName())=="value")
        {
            parse_elementdata(elem_item, devicetypeproperty.value());
        }
        else if (QString(elem_item.fieldName())=="default_value")
        {
            parse_elementdata(elem_item, devicetypeproperty.default_value());
        }
        else if (QString(elem_item.fieldName())=="extra_data")
        {
            devicetypeproperty.extra_data() = string_to_QString(elem_item.String());
        }
        else if (QString(elem_item.fieldName())=="property_type")
        {
            devicetypeproperty.property_type() = 
                nr_device_type_property::parse_property_type(string_to_QString(elem_item.String()));
        }
        else if (QString(elem_item.fieldName())=="value_type")
        {
            devicetypeproperty.value_type() = 
                nr_object::parser_value_type(string_to_QString(elem_item.String()));
        }
        else if (QString(elem_item.fieldName())=="children")
        {
            BSONObj obj_children = elem_item.Obj();
            BSONObjIterator it_child(obj_children);
            while (it_child.more())
            {
                BSONElement elem_child = it_child.next();
                nr_device_type_property *devicetypeproperty_child = new nr_device_type_property;
                bool ret = read_document_Item( elem_child , *devicetypeproperty_child );
                Q_ASSERT(ret);
                devicetypeproperty.children().insert(devicetypeproperty_child->id(),devicetypeproperty_child);
            }
        }
    }

    return true;
}

bool nr_io_mongodb::read_document_DeviceType( BSONObj& obj, nr_device_type& devicetype )
{
    // Find each element
    BSONObjIterator it(obj);
    while(it.more())
    {
        BSONElement elem = it.next();
        if (QString(elem.fieldName())=="id")
        {
            devicetype.id() = elem.numberInt();
        }
        else if (QString(elem.fieldName())=="name")
        {
            //QString::fromStdString(elem.String());
            devicetype.name() = string_to_QString(elem.String());
        }
        else if (QString(elem.fieldName())=="text")
        {
            devicetype.text() = string_to_QString(elem.String());
        }
        else if (QString(elem.fieldName())=="category")
        {
            devicetype.category() = string_to_QString(elem.String());
        }
        else if (QString(elem.fieldName())=="description")
        {
            devicetype.description() = string_to_QString(elem.String());
        }
        else if (QString(elem.fieldName())=="module")
        {
            devicetype.module() = string_to_QString(elem.String());
        }
        else if (QString(elem.fieldName())=="properties")
        {
            BSONObj obj_properties = elem.Obj();
            BSONObjIterator it_property(obj_properties);
            while(it_property.more())
            {
                BSONElement elem_property = it_property.next();
                nr_device_type_property *devicetypeproperty = new nr_device_type_property;
                bool ret = read_document_Item( elem_property , *devicetypeproperty );
                Q_ASSERT(ret);
                devicetype.properties().insert(devicetypeproperty->id(),devicetypeproperty);
            }
        }
        else if (QString(elem.fieldName())=="parameters")
        {
            BSONObj obj_parameters = elem.Obj();
            BSONObjIterator it_parameter(obj_parameters);
            while(it_parameter.more())
            {
                BSONElement elem_prarameter = it_parameter.next();
                nr_device_type_parameter *devicetypeprarameter = new nr_device_type_parameter;
                bool ret = read_document_Item( elem_prarameter , *devicetypeprarameter );
                Q_ASSERT(ret);
                devicetype.parameters().insert(devicetypeprarameter->id(),devicetypeprarameter);
            }
        }
        else if (QString(elem.fieldName())=="events")
        {
            BSONObj obj_events = elem.Obj();
            BSONObjIterator it_event(obj_events);
            while(it_event.more())
            {
                BSONElement elem_event = it_event.next();
                nr_device_type_event *devicetypeevent = new nr_device_type_event;
                bool ret = read_document_Item( elem_event , *devicetypeevent );
                Q_ASSERT(ret);
                devicetype.events().insert(devicetypeevent->id(),devicetypeevent);
            }
        }
        else if (QString(elem.fieldName())=="child_types")
        {
            BSONObj obj_child_types = elem.Obj();
            BSONObjIterator it_property(obj_child_types);
            while (it_property.more())
            {
                BSONElement elem_child_types = it_property.next();
                BSONObj obj_child_types_2 = elem_child_types.Obj();
                BSONObjIterator it_property_2(obj_child_types_2);
                while (it_property_2.more())
                {
                    BSONElement elem_child_types_2 = it_property_2.next();

                    if (QString(elem_child_types_2.fieldName())=="id")
                    {
                        int sub_type_id = elem_child_types_2.numberInt();
                        Q_ASSERT(!devicetype.child_types().contains(sub_type_id));
                        devicetype.child_types().insert(sub_type_id);
                    }
                }
            }
        }
    }


    return true;
}

BSONObjBuilder& nr_io_mongodb::write_document_Item(nr_device_type_property* device_type_property)
{
    BSONObjBuilder *objbuilder = new BSONObjBuilder;
    BSONObjBuilder &objbuilder_Property = *objbuilder;
    append_variant(objbuilder_Property, "id", device_type_property->id());
    append_variant(objbuilder_Property, "name", device_type_property->name());
    append_variant(objbuilder_Property, "text", device_type_property->text());
    if ( QString(device_type_property->category()) != "" )
        append_variant(objbuilder_Property, "category", device_type_property->category());
    if ( QString(device_type_property->description()) != "" )
        append_variant(objbuilder_Property, "description", device_type_property->description());
    if ( QString(device_type_property->value().toString()) != "" )
        append_variant(objbuilder_Property, "value", device_type_property->value());
    if ( QString(device_type_property->default_value().toString()) != "" )
        append_variant(objbuilder_Property, "default_value", device_type_property->default_value());
    if ( QString(device_type_property->extra_data()) != "" )
        append_variant(objbuilder_Property, "extra_data", device_type_property->extra_data());
    if ( QString(device_type_property->toString_property_type()) != "" 
        && device_type_property->property_type() != nr_device_type_property::property_type_t(0) )
        append_variant(objbuilder_Property, "property_type", device_type_property->toString_property_type());
    if ( QString(device_type_property->toString_value_type()) != "" 
        && device_type_property->value_type() != nr_object::value_type_t(0))
        append_variant(objbuilder_Property, "value_type", device_type_property->toString_value_type());

    objbuilder = new BSONObjBuilder;
    BSONObjBuilder &objbuilder_children = *objbuilder;
    foreach(nr_device_type_property* child_property, device_type_property->children())
    {
        BSONObjBuilder& ItemChild_objbuilder = write_document_Item(child_property);
        append_BSONObj(objbuilder_children, QString::number(child_property->id()), ItemChild_objbuilder.obj()); 
    }
    append_BSONObj(objbuilder_Property, "children", objbuilder_children.obj());

    return objbuilder_Property;
}

BSONObjBuilder& nr_io_mongodb::write_document_SubDeviceType(nr_id_t SubDevice_id)
{
    BSONObjBuilder *objbuilder = new BSONObjBuilder;
    BSONObjBuilder &objbuilder_SubDeviceType = *objbuilder;
    append_variant(objbuilder_SubDeviceType, "id", SubDevice_id);
    return objbuilder_SubDeviceType;
}

BSONObjBuilder& nr_io_mongodb::write_document_DeviceType(nr_device_type* devicetype)
{
    BSONObjBuilder *objbuilder = new BSONObjBuilder;
    BSONObjBuilder &objbuilder_DeviceType = *objbuilder;
    append_variant(objbuilder_DeviceType, "id",         devicetype->id());
    append_variant(objbuilder_DeviceType, "name",       devicetype->name());
    append_variant(objbuilder_DeviceType, "text",       devicetype->text());
    append_variant(objbuilder_DeviceType, "category",   devicetype->category());
    append_variant(objbuilder_DeviceType, "description",devicetype->description());
    if ( QString(devicetype->module()) != "" )
        append_variant(objbuilder_DeviceType, "module",     devicetype->module());

    objbuilder = new BSONObjBuilder;
    BSONObjBuilder &objbuilder_properties = *objbuilder;
    foreach(nr_device_type_property* child_property, devicetype->properties())
    {
        BSONObjBuilder& Item_objbuilder = write_document_Item(child_property);
        append_BSONObj(objbuilder_properties, QString::number(child_property->id()), Item_objbuilder.obj());
    }
    append_BSONObj(objbuilder_DeviceType, "properties", objbuilder_properties.obj());//Properties

    objbuilder = new BSONObjBuilder;
    BSONObjBuilder &objbuilder_parameters = *objbuilder;
    foreach(nr_device_type_parameter* child_parameter, devicetype->parameters())
    {
        BSONObjBuilder& Item_objbuilder = write_document_Item(child_parameter);
        append_BSONObj(objbuilder_parameters, QString::number(child_parameter->id()), Item_objbuilder.obj());
    }
    append_BSONObj(objbuilder_DeviceType, "parameters", objbuilder_parameters.obj());//Parameters

    objbuilder = new BSONObjBuilder;
    BSONObjBuilder &objbuilder_events = *objbuilder;
    foreach(nr_device_type_parameter* child_event, devicetype->events())
    {
        BSONObjBuilder& Item_objbuilder = write_document_Item(child_event);
        append_BSONObj(objbuilder_events, QString::number(child_event->id()), Item_objbuilder.obj());
    }
    append_BSONObj(objbuilder_DeviceType, "events", objbuilder_events.obj());//Events

    objbuilder = new BSONObjBuilder;
    BSONObjBuilder &objbuilder_child_types = *objbuilder;
    for (QSet<nr_id_t>::iterator subdeviceid_iterator = devicetype->child_types().begin();
        subdeviceid_iterator != devicetype->child_types().end() ; ++subdeviceid_iterator )
    {
        BSONObjBuilder& SubDeviceType_objbuilder = write_document_SubDeviceType(*subdeviceid_iterator);
        append_BSONObj(objbuilder_child_types, QString::number(*subdeviceid_iterator), SubDeviceType_objbuilder.obj());
    }
    append_BSONObj(objbuilder_DeviceType, "child_types", objbuilder_child_types.obj());//SubDeviceType

    return objbuilder_DeviceType;
}

bool nr_io_mongodb::read_document_Property( BSONObj& obj, nr_device_property& device_property )
{
    BSONObjIterator it(obj);
    while (it.more())
    {
        BSONElement elem = it.next();
        if (QString(elem.fieldName())=="id")
        {
            device_property.id() = elem.numberInt();
        }
        else if (QString(elem.fieldName())=="value")
        {
            parse_elementdata(elem, device_property.value());
        }
        else if (QString(elem.fieldName())=="children")
        {
            BSONObj obj_properties_children = elem.Obj();
            BSONObjIterator it_property(obj_properties_children);
            while(it_property.more())
            {
                BSONElement elem_property = it_property.next();
                nr_device_property* child_device_property = new nr_device_property;
                bool ret = read_document_Property( elem_property.Obj() , *child_device_property );
                Q_ASSERT(ret);
                device_property.children().insert(child_device_property->id(), child_device_property);
            }
        }
        else if (QString(elem.fieldName())=="property_type")
        {
            device_property.property_type() = 
                nr_device_property::parse_property_type( string_to_QString(elem.String()) );
        }
    }

    return true;
}

bool nr_io_mongodb::read_document_DeviceItem( BSONObj& obj, nr_device_item& device_item )
{
    BSONObjIterator it(obj);
    while (it.more())
    {
        BSONElement elem = it.next();
        if (QString(elem.fieldName())=="id")
        {
            device_item.id() = string_to_QString(elem.String());
        }
        else if (QString(elem.fieldName())=="name")
        {
            device_item.name() = string_to_QString(elem.String());
        }
        else if (QString(elem.fieldName())=="type_id")
        {
            device_item.device_type() = this->get_device_type(elem.numberInt());
        }
        else if (QString(elem.fieldName())=="properties")
        {
            BSONObj obj_properties_children = elem.Obj();
            BSONObjIterator it_property(obj_properties_children);
            while(it_property.more())
            {
                BSONElement elem_property = it_property.next();
                nr_device_property* child_device_property = new nr_device_property;
                bool ret = read_document_Property( elem_property.Obj() , *child_device_property );
                Q_ASSERT(ret);
                device_item.properties().insert(child_device_property->id(), child_device_property);
            }
        }
        else if (QString(elem.fieldName())=="items")
        {
            BSONObj obj_items_children = elem.Obj();
            BSONObjIterator it_item(obj_items_children);
            while (it_item.more())
            {
                BSONElement elem_item = it_item.next();
                nr_device_item* child_device_item = new nr_device_item;
                bool ret = read_document_DeviceItem( elem_item.Obj() , *child_device_item );
                Q_ASSERT(ret);
                child_device_item->parent_item() = &device_item;
                device_item.items().insert(child_device_item->id(), child_device_item);
            }
        }
    }

    return true;
}

bool nr_io_mongodb::read_document_DeviceGroup( BSONObj& obj, nr_device_group& device_group, nr_device_item &parent_device_item )
{
    BSONObjIterator it(obj);
    while(it.more())
    {
        BSONElement elem = it.next();
        if (QString(elem.fieldName())=="name")
        {
            device_group.name() = string_to_QString(elem.String());
        }
        else if (QString(elem.fieldName())=="groups")
        {
            BSONObj obj_groups_children = elem.Obj();
            BSONObjIterator it_group(obj_groups_children);
            while(it_group.more())
            {
                BSONElement elem_group = it_group.next();
                nr_device_group* child_device_group = new nr_device_group;
                bool ret = read_document_DeviceGroup( elem_group.Obj() , *child_device_group , parent_device_item );
                Q_ASSERT(ret);
                device_group.groups().insert(child_device_group->name(), child_device_group);
            }
        }
        else if (QString(elem.fieldName())=="items")
        {
            BSONObj obj_items_children = elem.Obj();
            BSONObjIterator it_item(obj_items_children);
            while (it_item.more())
            {
                BSONElement elem_item = it_item.next();
                nr_device_item* child_device_item = new nr_device_item;
                bool ret = read_document_DeviceItem( elem_item.Obj() , *child_device_item );
                Q_ASSERT(ret);
                child_device_item->parent_item() = &parent_device_item;
                device_group.items().insert(child_device_item->id(), child_device_item);
            }
        }
    }

    return true;
}

bool nr_io_mongodb::read_document_DeviceItemServer( BSONObj& obj, nr_device_item_server& device_item_server )
{
    // Find each element
    BSONObjIterator it(obj);
    while(it.more())
    {
        BSONElement elem = it.next();
        if (QString(elem.fieldName())=="id")
        {
            device_item_server.id() = string_to_QString(elem.String());
        }
        else if (QString(elem.fieldName())=="name")
        {
            device_item_server.name() = string_to_QString(elem.String());
        }
        else if (QString(elem.fieldName())=="type_id")
        {
            device_item_server.device_type() = this->get_device_type(elem.numberInt());
        }
        else if (QString(elem.fieldName())=="groups")
        {
            BSONObj obj_groups = elem.Obj();
            BSONObjIterator it_group(obj_groups);
            while(it_group.more())
            {
                BSONElement elem_group = it_group.next();
                nr_device_group* device_group = new nr_device_group;
                bool ret = read_document_DeviceGroup( elem_group.Obj() , *device_group , device_item_server);
                Q_ASSERT(ret);
                device_item_server.groups().insert(device_group->name(), device_group);
            }
        }
    }

    return true;
}

BSONObjBuilder& nr_io_mongodb::write_document_Property( nr_device_property* deviceproperty )
{
    BSONObjBuilder *objbuilder = new BSONObjBuilder;
    BSONObjBuilder &objbuilder_device_property = *objbuilder;
    append_variant(objbuilder_device_property, "id", deviceproperty->id());
    if ( deviceproperty->value().toString()!="" )
        append_variant(objbuilder_device_property, "value", deviceproperty->value());
    if ( deviceproperty->property_type_to_string()!="" )
        append_variant(objbuilder_device_property, "property_type", deviceproperty->property_type_to_string());

    objbuilder = new BSONObjBuilder;
    BSONObjBuilder &children_objbuilder_properties = *objbuilder;
    foreach(nr_device_property* child_property, deviceproperty->children())
    {
        BSONObjBuilder& child_property_objbuilder = write_document_Property(child_property);
        append_BSONObj(children_objbuilder_properties, QString::number(child_property->id()), child_property_objbuilder.obj());
    }
    append_BSONObj(objbuilder_device_property, "children", children_objbuilder_properties.obj());//DeviceGroup

    return objbuilder_device_property;
}

BSONObjBuilder& nr_io_mongodb::write_document_DeviceItem( nr_device_item* deviceitem )
{
    BSONObjBuilder *objbuilder = new BSONObjBuilder;
    BSONObjBuilder &objbuilder_device_item = *objbuilder;
    append_variant(objbuilder_device_item, "id", deviceitem->id());
    append_variant(objbuilder_device_item, "name", deviceitem->name());
    append_variant(objbuilder_device_item, "type_id", deviceitem->device_type()->id());

    objbuilder = new BSONObjBuilder;
    BSONObjBuilder &children_objbuilder_properties = *objbuilder;
    foreach(nr_device_property* child_property, deviceitem->properties())
    {
        BSONObjBuilder& child_property_objbuilder = write_document_Property(child_property);
        append_BSONObj(children_objbuilder_properties, QString::number(child_property->id()), child_property_objbuilder.obj());
    }
    append_BSONObj(objbuilder_device_item, "properties", children_objbuilder_properties.obj());//DeviceGroup

    objbuilder = new BSONObjBuilder;
    BSONObjBuilder &children_objbuilder_items = *objbuilder;
    foreach(nr_device_item* child_deviceitem, deviceitem->items())
    {
        BSONObjBuilder& child_item_objbuilder = write_document_DeviceItem(child_deviceitem);
        append_BSONObj(children_objbuilder_items, child_deviceitem->id(), child_item_objbuilder.obj());
    }
    append_BSONObj(objbuilder_device_item, "items", children_objbuilder_items.obj());

    return objbuilder_device_item;
}

BSONObjBuilder& nr_io_mongodb::write_document_DeviceGroup( nr_device_group* devicegroup )
{
    BSONObjBuilder *objbuilder = new BSONObjBuilder;
    BSONObjBuilder &objbuilder_device_group = *objbuilder;
    append_variant(objbuilder_device_group, "name", devicegroup->name());

    objbuilder = new BSONObjBuilder;
    BSONObjBuilder &children_objbuilder_groups = *objbuilder;
    foreach(nr_device_group* child_devicegroup, devicegroup->groups())
    {
        BSONObjBuilder& child_group_objbuilder = write_document_DeviceGroup(child_devicegroup);
        append_BSONObj(children_objbuilder_groups, child_devicegroup->name(), child_group_objbuilder.obj());
    }
    append_BSONObj(objbuilder_device_group, "groups", children_objbuilder_groups.obj());//DeviceGroup

    objbuilder = new BSONObjBuilder;
    BSONObjBuilder &children_objbuilder_items = *objbuilder;
    foreach(nr_device_item* child_deviceitem, devicegroup->items())
    {
        BSONObjBuilder& child_item_objbuilder = write_document_DeviceItem(child_deviceitem);
        append_BSONObj(children_objbuilder_items, child_deviceitem->id(), child_item_objbuilder.obj());
    }
    append_BSONObj(objbuilder_device_group, "items", children_objbuilder_items.obj());

    return objbuilder_device_group;
}

BSONObjBuilder& nr_io_mongodb::write_document_DeviceItemServer( nr_device_item_server* item_server )
{
    BSONObjBuilder *objbuilder = new BSONObjBuilder;
    BSONObjBuilder &objbuilder_DeviceItemServer = *objbuilder;
    append_variant(objbuilder_DeviceItemServer, "id", item_server->id());
    append_variant(objbuilder_DeviceItemServer, "name", item_server->name());
    append_variant(objbuilder_DeviceItemServer, "type_id", item_server->device_type()->id());

    objbuilder = new BSONObjBuilder;
    BSONObjBuilder &objbuilder_groups = *objbuilder;
    foreach(nr_device_group* devicegroup, item_server->groups())
    {
        BSONObjBuilder& group_objbuilder = write_document_DeviceGroup(devicegroup);
        append_BSONObj(objbuilder_groups, devicegroup->name(), group_objbuilder.obj());
    }
    append_BSONObj(objbuilder_DeviceItemServer, "groups", objbuilder_groups.obj());//DeviceGroup

    return objbuilder_DeviceItemServer;
}

// Only used to eliminate the miss symbol link error
namespace boost
{
    void tss_cleanup_implemented() {}
}
